/* Copyright 2007-2008 dnAnalytics Project.
 *
 * Contributors to this file:
 * Patrick van der Velde
 * Marcus Cuda - http://marcus.cuda.net
 *
 * This file is part of dnAnalytics.  dnAnalytics is licensed under the 
 * Microsoft Public License. See License.txt for a complete copy of the
 * license.
 */

namespace dnAnalytics.Math
{
    ///<summary>Provides trigonometric, logarithmic, and other common mathematical functions for complex numbers.</summary>
    public static partial class ComplexMath
    {
        /// <summary>Returns the absolute value of a complex number as a Euclidean norm.</summary>
        /// <returns>The absolute value (Euclidean norm) of the complex number.</returns>
        /// <param name="value">The complex number to compute the absolute value from.</param>
        ///Based off Numerical Recipes' Cabs.
        public static double Absolute(Complex value)
        {
            if (Complex.IsNaN(value))
            {
                return double.NaN;
            }
            if (Complex.IsInfinity(value))
            {
                return double.PositiveInfinity;
            }

            double real = System.Math.Abs(value.Real);
            double imag = System.Math.Abs(value.Imaginary);
            if (value.Real == 0)
            {
                return imag;
            }
            if (value.Imaginary == 0)
            {
                return real;
            }
            if (real > imag)
            {
                double temp = imag / real;
                return real * System.Math.Sqrt(1.0 + temp * temp);
            }
            else
            {
                double temp = real / imag;
                return imag * System.Math.Sqrt(1.0 + temp * temp);
            }
        }

        /// <summary>Calculates the argument of a complex number.</summary>
        /// <returns>The argument of the complex number.</returns>
        /// <param name="value">The complex number to compute the argument from.</param>
        public static double Argument(Complex value)
        {
            return System.Math.Atan2(value.Imaginary, value.Real);
        }


        /// <summary>Returns the conjugate of a complex number.</summary>
        /// <returns>The conjugate of the complex number.</returns>
        /// <param name="value">The complex value to compute the conjugate of.</param>
        public static Complex Conjugate(Complex value)
        {
            return new Complex(value.Real, -value.Imaginary);
        }

        /// <summary>Returns the complex number with the larger norm.</summary>
        /// <returns>The complex number with the larger norm.</returns>
        /// <param name="v1">One of the complex numbers to compare.</param>
        /// <param name="v2">The other complex number to compare.</param>
        public static Complex Max(Complex v1, Complex v2)
        {
            return Norm(v1) >= Norm(v2) ? v1 : v2;
        }

        /// <summary>Returns the complex number with the smaller norm.</summary>
        /// <returns>The complex number with the smaller norm.</returns>
        /// <param name="v1">One of the complex numbers to compare.</param>
        /// <param name="v2">The other complex number to compare.</param>
        public static Complex Min(Complex v1, Complex v2)
        {
            return Norm(v1) <= Norm(v2) ? v1 : v2;
        }

        /// <summary>Returns the Euclidean norm of a complex number.</summary>
        /// <returns>The Euclidean norm of the complex number.</returns>
        /// <param name="value">The complex number to compute the Euclidean norm of.</param>
        public static double Norm(Complex value)
        {
            return Absolute(value);
        }

        /// <summary>Returns the polar representation of a complex number.</summary>
        /// <returns>The polar representation of the complex number.</returns>
        /// <param name="value">The complex number to compute the polar value of.</param>
        public static Complex Polar(Complex value)
        {
            return new Complex(Absolute(value), System.Math.Atan2(value.Imaginary, value.Real));
        }

        /// <summary>Returns the absolute value of a Complex32 number as a Euclidean norm.</summary>
        /// <returns>The absolute value (Euclidean norm) of the Complex32 number.</returns>
        /// <param name="value">The Complex32 number to compute the absolute value from.</param>
        //Based off Numerical Recipes' Cabs.
        public static float Absolute(Complex32 value)
        {
            if (Complex32.IsNaN(value))
            {
                return float.NaN;
            }
            if (Complex32.IsInfinity(value))
            {
                return float.PositiveInfinity;
            }

            float real = System.Math.Abs(value.Real);
            float imag = System.Math.Abs(value.Imaginary);
            if (value.Real == 0)
            {
                return imag;
            }
            if (value.Imaginary == 0)
            {
                return real;
            }
            if (real > imag)
            {
                double temp = imag / real;
                return (float)(real * System.Math.Sqrt(1.0 + temp * temp));
            }
            else
            {
                double temp = real / imag;
                return (float)(imag * System.Math.Sqrt(1.0 + temp * temp));
            }
        }

        /// <summary>Calculates the argument of a Complex32 number.</summary>
        /// <returns>The argument of the Complex32 number.</returns>
        /// <param name="value">The Complex32 number to compute the argument from.</param>
        public static float Argument(Complex32 value)
        {
            return (float)System.Math.Atan2(value.Imaginary, value.Real);
        }

        /// <summary>Returns the conjugate of a Complex32 number.</summary>
        /// <returns>The conjugate of the Complex32 number.</returns>
        /// <param name="value">The Complex32 value to compute the conjugate of.</param>
        public static Complex32 Conjugate(Complex32 value)
        {
            return new Complex32(value.Real, -value.Imaginary);
        }


        /// <summary>Returns the Complex32 number with the larger norm.</summary>
        /// <returns>The Complex32 number with the larger norm.</returns>
        /// <param name="v1">One of the Complex32 numbers to compare.</param>
        /// <param name="v2">The other Complex32 number to compare.</param>
        public static Complex32 Max(Complex32 v1, Complex32 v2)
        {
            return Norm(v1) >= Norm(v2) ? v1 : v2;
        }

        /// <summary>Returns the Complex32 number with the smaller norm.</summary>
        /// <returns>The Complex32 number with the smaller norm.</returns>
        /// <param name="v1">One of the Complex32 numbers to compare.</param>
        /// <param name="v2">The other Complex32 number to compare.</param>
        public static Complex32 Min(Complex32 v1, Complex32 v2)
        {
            return Norm(v1) <= Norm(v2) ? v1 : v2;
        }

        /// <summary>Returns the Euclidean norm of a Complex32 number.</summary>
        /// <returns>The Euclidean norm of the Complex32 number.</returns>
        /// <param name="value">The Complex32 number to compute the Euclidean norm of.</param>
        public static float Norm(Complex32 value)
        {
            return Absolute(value);
        }

        /// <summary>Returns the polar representation of a Complex32 number.</summary>
        /// <returns>The polar representation of the Complex32 number.</returns>
        /// <param name="value">The Complex32 number to compute the polar value of.</param>
        public static Complex32 Polar(Complex32 value)
        {
            return new Complex32(Absolute(value), (float) System.Math.Atan2(value.Imaginary, value.Real));
        }
    }
}